/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package io.iec.edp.caf.multicontext.classloader;

import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.multicontext.support.JarUtil;
import lombok.var;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.loader.LaunchedURLClassLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import sun.misc.URLClassPath;
import sun.net.www.protocol.file.FileURLConnection;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.jar.JarFile;

/**
 * 子模块类加载器
 * <p>
 * 1、类加载器新增了getModuleResources方法，用于从自己私有的ucp中查找资源
 *
 * @author guowenchang
 */
public class ModuleClassloader extends LaunchedURLClassLoader {

    private final URLClassPath moduleUcp;

    private LaunchedURLClassLoader resourceLoader;

    private WeakHashMap<Closeable, Void> closeables = new WeakHashMap();

    /**
     * Create a new {@link LaunchedURLClassLoader} instance.
     *
     * @param urls   the URLs from which to load classes and resources
     * @param parent the parent class loader for delegation
     */
    public ModuleClassloader(URL[] urls, PlatformClassloader parent) {
        /**
         * 目的：1. 所有的class都通过PlatformClassLoader加载，以避免模块间class相互访问不到，造成Hibernate等第三方组件报错
         *      2. 除class外，模块可以负责加载自己的资源
         * 做法：1. 调用super时不传递url列表，防止类被自身classLoader加载（当然，按loadClass加载逻辑，传进去问题也不大）
         *      2. 内部保留一份URLClassPath，并重写getResource等相关方法，实现自定义模块资源读取
         */
        super(new URL[0], parent);
        addUrl(parent, urls);
        moduleUcp = new URLClassPath(urls);
        //这里先把所有的子模块都加上
        //todo 只给需要子模块加
        urls = mergeURL(urls);
        resourceLoader = new LaunchedURLClassLoader(urls, null);

    }

    private URL[] mergeURL(URL[] urls){
        var list = new ArrayList<>();
        for (var u : urls){
            list.add(u);
        }

        var path = CafEnvironment.getRuntimePath()+"/libs/caf-multicontext-surport.jar";
        Collections.addAll(list,JarUtil.getJarPathFromDirPaths(path));
        urls = list.toArray(new URL[0]);

        //这里不能先获取，一担获取会有缓存，要是获取了先删除缓存也行
//        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,this);
//        if(configurations==null || configurations.size()==0){
//            var list = new ArrayList<>();
//            for (var u : urls){
//                list.add(u);
//            }
//
//            var path = CafEnvironment.getRuntimePath()+"/libs/caf-multicontext-surport.jar";
//            Collections.addAll(list,JarUtil.getJarPathFromDirPaths(path));
//            urls = list.toArray(new URL[0]);
//            resourceLoader = new LaunchedURLClassLoader(urls, null);
//        }
        return urls;
    }

    private void addUrl(PlatformClassloader classloader, URL[] urls) {
        classloader.addModuleURL(urls);
    }

    public Enumeration<URL> getModuleResources(String path) {
        return moduleUcp.findResources(path, true);
    }

    @Override
    public URL getResource(String var1) {
        return this.findResource(var1);
    }

    @Override
    public Enumeration<URL> getResources(String name) throws IOException {
        return this.findResources(name);
    }

    @Override
    public URL findResource(String var1) {
        return resourceLoader.findResource(var1);
    }

    @Override
    public Enumeration<URL> findResources(String var1) throws IOException {
        return resourceLoader.findResources(var1);
    }
    @Override
    public InputStream getResourceAsStream(String var1) {
        URL var2 = super.getResource(var1);

        try {
            if (var2 == null) {
                return null;
            } else {
                URLConnection var3 = var2.openConnection();
                InputStream var4 = var3.getInputStream();
                if (var3 instanceof JarURLConnection) {
                    JarURLConnection var5 = (JarURLConnection) var3;
                    JarFile var6 = var5.getJarFile();
                    synchronized (this.closeables) {
                        if (!this.closeables.containsKey(var6)) {
                            this.closeables.put(var6, null);
                        }
                    }
                } else if (var3 instanceof FileURLConnection) {
                    synchronized (this.closeables) {
                        this.closeables.put(var4, null);
                    }
                }

                return var4;
            }
        } catch (IOException var12) {
            return null;
        }
    }
}
